// CTgaLoader.cpp -- standard vector class
// *** Code "Borrowed" from www.UltimateGameProgramming.com
#include "CVector.h"

#define PI_VALUE 3.141592654


CVector3::CVector3()
{
   // Initialize the variables to 0.
   x = y = z = 0.0;
}


CVector3::CVector3(float X, float Y, float Z)
{
   // Initialize the varibles to the data in X, Y, and Z.
   x = X;
   y = Y;
   z = Z;
}

CVector3::CVector3(const CVector3 &v)
{
   // Initialize this object to v.
   x = v.x;
   y = v.y;
   z = v.z;
}


void CVector3::operator =(CVector3 v)
{
   // Make this objects x, y, and z equal to the object on the right of the = sign.
   x = v.x;
   y = v.y;
   z = v.z;
}


CVector3 CVector3::operator -(CVector3 v)
{
   // Return the value of this vector - v.
   return CVector3(x - v.x, y - v.y, z - v.z);
}


CVector3 CVector3::operator +(CVector3 v)
{
   // Return the value of this vector + v.
   return CVector3(x + v.x, y + v.y, z + v.z);
}


CVector3 CVector3::operator *(CVector3 v)
{
   // Return the value of this vector * v.
   return CVector3(x * v.x, y * v.y, z * v.z);
}


CVector3 CVector3::operator /(CVector3 v)
{
   // Return the value of this vector / v.
   return CVector3(x / v.x, y / v.y, z / v.z);
}


CVector3 CVector3::operator +(float f)
{
   // Return the value of this vector + f.
   return CVector3(x + f, y + f, z + f);
}


CVector3 CVector3::operator -(float f)
{
   // Return the value of this vector - f.
   return CVector3(x - f, y - f, z - f);
}


CVector3 CVector3::operator *(float f)
{
   // Return the value of this vector * f.
   return CVector3(x * f, y * f, z * f);
}


CVector3 CVector3::operator /(float f)
{
   // Return the value of this vector / f.  We do this by multiplying the recip.
   f = 1/f;

   return CVector3(x * f, y * f, z * f);
}


void CVector3::operator +=(CVector3 v)
{
   // Add this by object v and store results here.
   x += v.x;
   y += v.y;
   z += v.z;
}


void CVector3::operator -=(CVector3 v)
{
   // Subtract this by object v and store results here.
   x -= v.x;
   y -= v.y;
   z -= v.z;
}


void CVector3::operator *=(CVector3 v)
{
   // Mul this by object v and store results here.
   x *= v.x;
   y *= v.y;
   z *= v.z;
}


void CVector3::operator /=(CVector3 v)
{
   // Divide this by object v and store results here.
   x /= v.x;
   y /= v.y;
   z /= v.z;
}


void CVector3::operator +=(float f)
{
   // Add this by object f and store results here.
   x += f;
   y += f;
   z += f;
}


void CVector3::operator -=(float f)
{
   // Subract this by object f and store results here.
   x -= f;
   y -= f;
   z -= f;
}


void CVector3::operator *=(float f)
{
   // Multiply this by object f and store results here.
   x *= f;
   y *= f;
   z *= f;
}


void CVector3::operator /=(float f)
{
   // Divide this by object f and store results here by multiplying by the recip.
   f = 1/f;

   x *= f;
   y *= f;
   z *= f;
}


float &CVector3::operator[](int index) {
   if(index==0)
   	return x;
  	if(index==1)
   	return y;
  	if(index==2)
   	return z;
}

float CVector3::operator[](int index) const {
   if(index==0)
   	return x;
  	if(index==1)
   	return y;
  	if(index==2)
   	return z;
}


bool CVector3::operator ==(CVector3 v)
{
   // Return true if all equal each other, false if one or more don't.
   return ((x == v.x) && (y== v.y) && (z == v.z));
}


bool CVector3::operator !=(CVector3 v)
{
   // Return true if one or all don't equal each other, false if they equal.
   return !((x == v.x) && (y== v.y) && (z == v.z));
}


void CVector3::CrossProduct(CVector3 v1, CVector3 v2)
{
   // Get the cross product of v1 and v2 and store it in this vector.
   x = ((v1.y * v2.z) - (v1.z * v2.y));
   y = ((v1.z * v2.x) - (v1.x * v2.z));
   z = ((v1.x * v2.y) - (v1.y * v2.x));
}


void CVector3::CrossProduct3(CVector3 v1, CVector3 v2, CVector3 v3)
{
 //  // Get the cross product of v1, v2, and v3.
//   x = v1.y * v2.z * v3.w +
//       v1.z * v2.w * v3.y +
//       v1.w * v2.y * v3.z - 
//       v1.y * v2.w * v3.z -
//       v1.z * v2.y * v3.w -
//       v1.w * v2.z * v3.y;
//
//   y = v1.x * v2.w * v3.z +
//       v1.z * v2.x * v3.w +
//       v1.w * v2.z * v3.x -
//       v1.x * v2.z * v3.w -
//       v1.z * v2.w * v3.x -
//       v1.w * v2.x * v3.z;
//
//   z = v1.x * v2.y * v3.w +
//       v1.y * v2.w * v3.x +
//       v1.w * v2.x * v3.y -
//       v1.x * v2.w * v3.y -
//       v1.y * v2.x * v3.w -
//       v1.w * v2.y * v3.x;
//
//   w = v1.x * v2.z * v3.y +
//       v1.y * v2.x * v3.z +
//       v1.z * v2.y * v3.x -
//       v1.x * v2.y * v3.z -
//       v1.y * v2.z * v3.x -
//       v1.z * v2.x * v3.y;
}


float CVector3::DotProduct3(CVector3 v1)
{
   // Get the dot product of v1 and this object and return it.
   return x * v1.x + y * v1.y + z * v1.z;
}

float CVector3::GetLength()
{
   // Return the length for this object.
   return (float)sqrt((x * x + y * y + z * z));
}


void CVector3::Normal()
{
   // Reduce this object to a unit vector.
   float lenght = GetLength();

   if(lenght == 0.0f)
      lenght = 1.0f;

   x = x/lenght;
   y = y/lenght;
   z = z/lenght;
}


void CVector3::Normalize(CVector3 Triangle[])
{
   // Normalize a triangle and store results in this object.
   CVector3 v1, v2;

   v1.x = Triangle[0].x - Triangle[1].x;
   v1.y = Triangle[0].y - Triangle[1].y;
   v1.z = Triangle[0].z - Triangle[1].z;

   v2.x = Triangle[1].x - Triangle[2].x;
   v2.y = Triangle[1].y - Triangle[2].y;
   v2.z = Triangle[1].z - Triangle[2].z;

   CrossProduct(v1, v2);
   Normal();
}

void CVector3::ExtendVertexPos(CVector3 currentVertex, CVector3 lightPos, float Extend)
{
    CVector3 lightDir;  // Direction of the light to the vertex position.
    CVector3 newPos;    // New extended vertex position to make up the shadow volume.

    // Get the light direction from the vertex position and the light position.
    lightDir = currentVertex - lightPos;

    // Now that we know where its going we add it to the position of the light so
    // we get the correct, new position.  We multiply it by a passed it value to
    // give the volume some distance or things won't come out quite as we want.
    newPos = lightPos + lightDir * Extend;

    x = newPos.x;
    y = newPos.y;
    z = newPos.z;
}


void CVector3::ExtendVertexPos(CVector3 lightPos, float Extend)
{
    CVector3 lightDir;  // Direction of the light to the vertex position.
    CVector3 newPos;    // New extended vertex position to make up the shadow volume.

    // Get the light direction from the vertex position and the light position.
    lightDir = CVector3(x, y, z) - lightPos;

    // Now that we know where its going we add it to the position of the light so
    // we get the correct, new position.  We multiply it by a passed it value to
    // give the volume some distance or things won't come out quite as we want.
    newPos = lightPos + lightDir * Extend;

    x = newPos.x;
    y = newPos.y;
    z = newPos.z;
}

CVector3 CVector3::GetRotatedAxis(double angle, CVector3 axis)
{
	if(angle == 0.0) return(*this);

   axis.Normal();

	CVector3 RotationRow1, RotationRow2, RotationRow3;

   double newAngle = PI_VALUE * angle / 180;
	float sinAngle = (float)sin(newAngle);
	float cosAngle = (float)cos(newAngle);
	float oneSubCos = 1.0f - cosAngle;

	RotationRow1.x = (axis.x) * (axis.x) + cosAngle * (1 - (axis.x) * (axis.x));
	RotationRow1.y = (axis.x) * (axis.y) * (oneSubCos) - sinAngle * axis.z;
	RotationRow1.z = (axis.x) * (axis.z) * (oneSubCos) + sinAngle * axis.y;

	RotationRow2.x = (axis.x) * (axis.y) * (oneSubCos) + sinAngle * axis.z;
	RotationRow2.y = (axis.y) * (axis.y) + cosAngle * (1 - (axis.y) * (axis.y));
	RotationRow2.z = (axis.y) * (axis.z) * (oneSubCos) - sinAngle * axis.x;
	
	RotationRow3.x = (axis.x) * (axis.z) * (oneSubCos) - sinAngle * axis.y;
	RotationRow3.y = (axis.y) * (axis.z) * (oneSubCos) + sinAngle * axis.x;
	RotationRow3.z = (axis.z) * (axis.z) + cosAngle * (1 - (axis.z) * (axis.z));

	return CVector3(this->DotProduct3(RotationRow1),
                   this->DotProduct3(RotationRow2),
                   this->DotProduct3(RotationRow3));
}


void CVector3::CalculateBinormalVector(CVector3 tangent, CVector3 normal)
{
   this->CrossProduct(tangent, normal);
}


void CVector3::ClampTo01()
{
   CVector3 temp(x, y, z);
   temp.Normal();
   temp = temp * 0.5f + CVector3(0.5f, 0.5f, 0.5f);

   x = temp.x;
   y = temp.y;
   z = temp.z;
}
